﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using DecisionFramework.Text;

namespace DecisionFramework.Instrument
{
	/// <summary>
	/// Represents performance metrics for an operation.
	/// </summary>
	/// <remarks>  
	/// <para>
	/// It keeps track of the name of the operation, total time,
	/// longest time, shortest time, and number of times the
	/// operation has been invoked. The average time can be computed
	/// from the total time / number of times invoked.
	/// </para>
	/// Author: Greg Cowin
	/// </remarks> 
	[DataContract]
	public class PerformanceMetrics
	{
        private static object myLock = new object();

		public PerformanceMetrics()
		{
			ShortestTimeInTicks = -1;
			CreatedUtc = DateTime.UtcNow;
		}

		/// <summary>
		/// The time in UTC that the metrics were created.
		/// </summary>
		[DataMember]
		public DateTime CreatedUtc { get; set; }

		/// <summary>
		/// The name of the operation being measured.
		/// </summary>
		[DataMember]
		public string OperationName { get; set; }

		/// <summary>
		/// The total time in ticks that have been taken
		/// for the operation.
		/// </summary>
		[DataMember]
		public long TotalTimeInTicks { get; set; }

		/// <summary>
		/// The longest time that the operation has taken.
		/// </summary>
		[DataMember]
		public long LongestTimeInTicks { get; set; }

		/// <summary>
		/// The shortest time in ticks that the operation has taken.
		/// </summary>
		[DataMember]
		public long ShortestTimeInTicks { get; set; }

		/// <summary>
		/// The number of times that the operation has been invoked
		/// since created.
		/// </summary>
		[DataMember]
		public long Count { get; set; }

        [DataMember]
        public string Details { get; set; }

		/// <summary>
		/// Record the operation time in ticks.
		/// </summary>
		/// <param name="timeInTicks">The number of ticks that the operation took.</param>
		internal void Record(long timeInTicks)
		{
            lock (myLock)
            {
                TotalTimeInTicks += timeInTicks;
                ++Count;
                if (ShortestTimeInTicks == -1 || timeInTicks < ShortestTimeInTicks)
                    ShortestTimeInTicks = timeInTicks;
                if (timeInTicks > LongestTimeInTicks)
                    LongestTimeInTicks = timeInTicks;
            }
		}

        internal void Reset()
        {
            lock (myLock)
            {
                TotalTimeInTicks = 0;
                Count = 0;
                ShortestTimeInTicks = 0;
                LongestTimeInTicks = 0;
            }
        }

		public override string ToString()
		{
			StringBuilder builder = new StringBuilder();
			builder.Append("Operation Name: ");
			builder.Append(OperationName);
			builder.Append("\nAverage Time: ");
			builder.Append(TextHelper.HowLong(new TimeSpan((long) (TotalTimeInTicks / Count) )));
			builder.Append("\nShortest Time: ");
			builder.Append(TextHelper.HowLong(new TimeSpan(ShortestTimeInTicks)));
			builder.Append("\nLongest Time: ");
			builder.Append(TextHelper.HowLong(new TimeSpan(LongestTimeInTicks)));
			builder.Append("\nTotal Time: ");
			builder.Append(TextHelper.HowLong(new TimeSpan(TotalTimeInTicks)));
			builder.Append("\nNumber of Times: ");
			builder.Append(Count);

            if (Details != null)
            {
                builder.Append("\nDetails: \n");
                builder.Append(Details);
            }

			return builder.ToString();
		}
	}
}
